package kim.artemis.rpc.core.model;

import kim.artemis.rpc.core.common.exception.ArtemisException;
import kim.artemis.rpc.core.protocol.client.ArtemisRpcClientInvoker;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 未来的响应
 * @author Wq.
 */
@Slf4j
public class ArtemisRpcFutureResponse implements Future<ArtemisRpcResponse> {

    private ArtemisRpcClientInvoker invokerFactory;

    private ArtemisRpcRequest request;
    private ArtemisRpcResponse response;

    private boolean doneFlag = false;
    private Object lock = new Object();

    public ArtemisRpcFutureResponse(ArtemisRpcClientInvoker invokerFactory, ArtemisRpcRequest request) {
        this.invokerFactory = invokerFactory;
        this.request = request;
        // 推送到未来响应池
        pushInvokerFuture();
    }


    public void pushInvokerFuture () {
        this.invokerFactory.pushInvokerFuture(request.getArtemisId(), this);
    }

    public void removeInvokerFuture () {
        this.invokerFactory.removeInvokerFuture(request.getArtemisId());
    }

    public void setArtemisRpcResponse (ArtemisRpcResponse artemisRpcResponse) {
        this.response = artemisRpcResponse;
        synchronized (lock) {
            doneFlag = true;
            lock.notifyAll();
        }
    }

    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
        return false;
    }

    @Override
    public boolean isCancelled() {
        return false;
    }

    @Override
    public boolean isDone() {
        return doneFlag;
    }

    @Override
    public ArtemisRpcResponse get() throws InterruptedException, ExecutionException {
        try {
            return get(-1, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            throw new ArtemisException(e);
        }
    }

    @Override
    public ArtemisRpcResponse get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        if (!doneFlag) {
            synchronized (lock) {
                try {
                    if (0 > timeout) {
                        lock.wait();
                    } else  {
                        long timeOutInterval =  (TimeUnit.MILLISECONDS == unit) ? timeout: TimeUnit.MILLISECONDS.convert(timeout, unit);
                        lock.wait(timeOutInterval);
                    }
                } catch (Exception e) {
                    throw new ArtemisException(e);
                }
            }
        }
        if (!doneFlag) {
            throw new ArtemisException("artemis rpc, request timeout at:" + System.currentTimeMillis() + ", request: " + request.toString());
        }
        return response;
    }

}
